// Copyright (C) 2018-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#pragma once

#include <memory>
#include <string>
#include <vector>
#include "frontend_manager_defs.hpp"

namespace ngraph
{
    namespace frontend
    {
        /// \brief An interface for identifying a place in a graph and iterate over it; can refer to
        /// an operation node, tensor, port etc.
        ///
        /// \note Each front end implementation provides specialization of this interface to
        /// represent a place in a model graph. Various methods in the front end classes accept and
        /// retrieve instances of Place to point to particular node part which should be modified or
        /// satisfies some criteria. For example, this class is used to report model inputs
        /// and outputs, for searching operations and tensors by name, for setting shape etc.
        ///
        /// Place can refer to Tensor, Input Edge, Input Port, Operation, Output Port, Output Edge
        ///
        ///                [Tensor A]
        ///                    |
        ///                    | [Input Edge]
        ///                    |
        ///                    V
        ///           -------------------
        ///           [  [Input Port 0] ]
        ///           [                 ]
        ///           [   Operation A   ]
        ///           [                 ]
        ///           [ [Output Port 0] ]
        ///           -------------------
        ///                    |
        ///                    | [Output Edge]
        ///                    |
        ///                    V
        ///                [Tensor B]
        ///                    |
        ///                    | [Input Edge]
        ///                    |
        ///                    V
        ///           -------------------
        ///           [  [Input Port 0] ]
        ///           [                 ]
        ///           [   Operation B   ]
        ///           [                 ]
        ///           [ [Output Port 0] ]
        ///           -------------------
        ///                    |
        ///                    | [Output Edge]
        ///                    |
        ///                    V
        ///                [Tensor C]
        ///
        class FRONTEND_API Place
        {
        public:
            typedef std::shared_ptr<Place> Ptr;

            virtual ~Place() = default;

            /// \brief All associated names (synonyms) that identify this place in the graph in a
            /// framework specific way
            ///
            /// \return A vector of strings each representing a name that identifies this place in
            /// the graph. Can be empty if there are no names associated with this place or name
            /// cannot be attached.
            virtual std::vector<std::string> get_names() const;

            /// \brief Returns references to all operation nodes that consume data from this place
            /// \note It can be called for any kind of graph place searching for the first consuming
            /// operations. It is optional if place has only one output port
            ///
            /// \return A vector with all operation node references that consumes data from this
            /// place
            virtual std::vector<Ptr> get_consuming_operations() const;

            /// \brief Returns references to all operation nodes that consume data from this place
            /// for specified output port
            ///
            /// \note It can be called for any kind of graph place searching for the first consuming
            /// operations.
            ///
            /// \param outputPortIndex If place is an operational node it specifies which output
            /// port should be considered.
            ///
            /// \return A vector with all operation node references that consumes data from this
            /// place
            virtual std::vector<Ptr> get_consuming_operations(int outputPortIndex) const;

            /// \brief Returns a tensor place that gets data from this place; applicable for
            /// operations, output ports and output edges which have only one output port
            ///
            /// \return A tensor place which hold the resulting value for this place
            virtual Ptr get_target_tensor() const;

            /// \brief Returns a tensor place that gets data from this place; applicable for
            /// operations, output ports and output edges
            ///
            /// \param outputPortIndex Output port index if the current place is an operation node
            /// and has multiple output ports
            ///
            /// \return A tensor place which hold the resulting value for this place
            virtual Ptr get_target_tensor(int outputPortIndex) const;

            /// \brief Returns a tensor place that supplies data for this place; applicable for
            /// operations, input ports and input edges which have only one input port
            ///
            /// \return A tensor place which supplies data for this place
            virtual Ptr get_source_tensor() const;

            /// \brief Returns a tensor place that supplies data for this place; applicable for
            /// operations, input ports and input edges
            ///
            /// \param inputPortIndex Input port index for operational nodes.
            ///
            /// \return A tensor place which supplies data for this place
            virtual Ptr get_source_tensor(int inputPortIndex) const;

            /// \brief Get an operation node place that immediately produces data for this place;
            /// applicable if place has only one input port
            ///
            /// \return An operation place that produces data for this place
            virtual Ptr get_producing_operation() const;

            /// \brief Get an operation node place that immediately produces data for this place
            ///
            /// \param inputPortIndex If a given place is itself an operation node, this specifies a
            /// port index
            ///
            /// \return An operation place that produces data for this place
            virtual Ptr get_producing_operation(int inputPortIndex) const;

            /// Returns a port that produces data for this place
            virtual Ptr get_producing_port() const;

            /// \brief For operation node returns reference to an input port; applicable if
            /// operation node has only one input port
            ///
            /// \return Input port place
            virtual Ptr get_input_port() const;

            /// \brief For operation node returns reference to an input port with specified index
            ///
            /// \param inputPortIndex Input port index
            ///
            /// \return Appropriate input port place
            virtual Ptr get_input_port(int inputPortIndex) const;

            /// \brief For operation node returns reference to an input port with specified name;
            /// applicable if port group has only one input port
            ///
            /// \param inputName Name of port group
            ///
            /// \return Appropriate input port place
            virtual Ptr get_input_port(const std::string& inputName) const;

            /// \brief For operation node returns reference to an input port with specified name and
            /// index
            ///
            /// \param inputName Name of port group, each group can have multiple ports
            ///
            /// \param inputPortIndex Input port index in a group
            ///
            /// \return Appropriate input port place
            virtual Ptr get_input_port(const std::string& inputName, int inputPortIndex) const;

            /// \brief For operation node returns reference to an output port; applicable for
            /// operations with only one output port
            ///
            /// \return Appropriate output port place
            virtual Ptr get_output_port() const;

            /// \brief For operation node returns reference to an output port with specified index
            ///
            /// \param outputPortIndex Output port index
            ///
            /// \return Appropriate output port place
            virtual Ptr get_output_port(int outputPortIndex) const;

            /// \brief For operation node returns reference to an output port with specified name;
            /// applicable if port group has only one output port
            ///
            /// \param outputName Name of output port group
            ///
            /// \return Appropriate output port place
            virtual Ptr get_output_port(const std::string& outputName) const;

            /// \brief For operation node returns reference to an output port with specified name
            /// and index
            ///
            /// \param outputName Name of output port group, each group can have multiple ports
            ///
            /// \param outputPortIndex Output port index
            ///
            /// \return Appropriate output port place
            virtual Ptr get_output_port(const std::string& outputName, int outputPortIndex) const;

            /// \brief Returns all input ports that consume data flows through this place
            virtual std::vector<Place::Ptr> get_consuming_ports() const;

            /// \brief Returns true if this place is input for a model.
            virtual bool is_input() const;

            /// \brief Returns true if this place is output for a model.
            virtual bool is_output() const;

            /// \brief Returns true if another place is the same as this place.
            ///
            /// \param another Another place object
            virtual bool is_equal(Ptr another) const;

            /// \brief Returns true if another place points to the same data.
            ///
            /// \note The same data means all places on path: output port -> output edge -> tensor
            /// -> input edge -> input port.
            ///
            /// \param another Another place object
            virtual bool is_equal_data(Ptr another) const;
        };
    } // namespace frontend
} // namespace ngraph
